/******************************************************************************
 *                                                                          
 * Copyright (c) 2008 Nuvoton Technology. All rights reserved.             
 *                                                                
 * 
 * FILENAME
 *     NUC900_Audio.c
 *
 * VERSION
 *     1.0 
 *
 * DESCRIPTION
 *
 *
 *
 *
 * DATA STRUCTURES
 *     None
 *
 * FUNCTIONS
 *
 *
 *     
 * HISTORY
 *     2004.07.27		Created by Yung-Chang Huang
 *
 *
 * REMARK
 *     None
 *
 *
 **************************************************************************/
#ifdef ECOS
#include "stdio.h"
#include "stdlib.h"
#include "kapi.h"
#include "diag.h"
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wblib.h"
#endif

#include "wbio.h"

#include "NUC900_Audio_Regs.h"
#include "NUC900_Audio.h"
//#include "AC97.h"

#define MSB_FORMAT	1
#define IIS_FORMAT  2	
#define NUM_API_DIRS          2

#define BITS_8_TO_16(x)      (short) ( ( (INT32) ( (UINT8)(x) - 128) ) << 8 )
#define BITS_16_TO_8(x)       ( (UINT8) (( (x) >> 8 ) +128))

UINT32 _uAuPlayBuffPHYAddr, _uAuPlayBuffUncachedAddr, _uAuPlayBuffSize;
UINT32 _uAuRecBuffPHYAddr, _uAuRecBuffUncachedAddr, _uAuRecBuffSize;
UINT32 _uAuPlayBuffOriginAddr;
UINT32 _uAuRecBuffOriginAddr;

UINT32 v_nVolume = 0xcccccccc;//default volume
AU_CB_FUNC_T	*g_fnWAVOutCallBack;
AU_CB_FUNC_T	*g_fnWAVInCallBack;

AU_DEV_E	_ePlayDev; 
AU_DEV_E _eRecDev;
BOOL g_fInUse[NUM_API_DIRS]; // is apidir in use? if so, fail Open calls

VOID
GetBufferM08 (PUINT8 pDstBuffer, PINT16 pSrcBuffer, UINT32 dwSamples)
{
    while (dwSamples-- > 0) {
        *pDstBuffer = (UINT8) BITS_16_TO_8(pSrcBuffer[0]);
        pSrcBuffer += 2;
        pDstBuffer += 1;
    }
}

VOID
GetBufferS08 (PUINT8 pDstBuffer, PINT16 pSrcBuffer, UINT32 dwSamples)
{
    while (dwSamples-- > 0) {
        pDstBuffer[0] = (UINT8) BITS_16_TO_8(pSrcBuffer[0]);
        pDstBuffer[1] = (UINT8) BITS_16_TO_8(pSrcBuffer[1]);
        pSrcBuffer += 2;
        pDstBuffer += 2;
    }
}


VOID
GetBufferM16 (PUINT8 pDst, PINT16 pSrcBuffer, UINT32 dwSamples)
{
    PINT16 pDstBuffer = (PINT16) pDst;
    while (dwSamples-- > 0) {
        pDstBuffer[0] = pSrcBuffer[0];
        pSrcBuffer += 2;
        pDstBuffer += 1;
    }
}


VOID
GetBufferS16 (PUINT8 pDstBuffer, PINT16 pSrcBuffer, UINT32 dwSamples)
{
    memcpy(pDstBuffer, pSrcBuffer, dwSamples * sizeof(UINT32));
}
VOID
FillBufferM08 (PINT16 pDstBuffer, PUINT8 pSrcBuffer, UINT32 dwSamples)
{
    while (dwSamples-- > 0) {
        PPCM_SAMPLE pSample = (PPCM_SAMPLE) pSrcBuffer;
        INT16 sample16 = BITS_8_TO_16(pSample->m8.sample);
        pDstBuffer[0] = sample16;
        pDstBuffer[1] = sample16;
        pDstBuffer += 2;
        pSrcBuffer += 1;
    }
}

VOID
FillBufferS08 (PINT16 pDstBuffer, PUINT8 pSrcBuffer, UINT32 dwSamples)
{
    while (dwSamples-- > 0) {
        PPCM_SAMPLE pSample = (PPCM_SAMPLE) pSrcBuffer;
        pDstBuffer[0] = BITS_8_TO_16(pSample->s8.sample_left);
        pDstBuffer[1] = BITS_8_TO_16(pSample->s8.sample_right);
        pDstBuffer += 2;
        pSrcBuffer += 2;
    }
}

VOID
FillBufferM16 (PINT16 pDstBuffer, PUINT8 pSrcBuffer, UINT32 dwSamples)
{
    while (dwSamples-- > 0) {
        INT16 sample = * (PINT16) pSrcBuffer;
        pDstBuffer[0] = sample;
        pDstBuffer[1] = sample;
        pDstBuffer += 2;
        pSrcBuffer += 2;
    }
}

VOID
FillBufferS16 (PINT16 pDstBuffer, PUINT8 pSrcBuffer, UINT32 dwSamples)
{
    memcpy(pDstBuffer, pSrcBuffer, dwSamples * sizeof(UINT32));
}

/* For align 32 */
static UINT32 shift_pointer(UINT32 ptr, UINT32 align)   
{
	UINT32 alignedPTR;
	UINT32 remain;
	
	//printf("pointer position is %x\n",ptr);
	if( (ptr%align)!=0)
	{
		remain = ptr % align;
		alignedPTR = ptr + (align - remain);
		return alignedPTR;
	}
	return ptr;
}

static BOOL audioAllocDMA(WAPI_INOUT apidir)
{
    BOOL bretval;

    if (apidir==WAPI_OUT)
    {
        _uAuPlayBuffSize = AUOUT_DMA_LEN;
        _uAuPlayBuffOriginAddr = _uAuPlayBuffPHYAddr = (UINT32)malloc(_uAuPlayBuffSize+32);
        _uAuPlayBuffPHYAddr = shift_pointer(_uAuPlayBuffOriginAddr,32);
#ifndef ECOS
        if (sysGetCacheState())
        {
            _uAuPlayBuffUncachedAddr = _uAuPlayBuffPHYAddr | 0x80000000;
        }
        else
            _uAuPlayBuffUncachedAddr = _uAuPlayBuffPHYAddr;
#else
		_uAuPlayBuffUncachedAddr = _uAuPlayBuffPHYAddr | 0x80000000;
#endif
        if (_uAuPlayBuffPHYAddr!=NULL)
            bretval = TRUE;
        else
            bretval = FALSE;
    }
    else
    {
        _uAuRecBuffSize = AUIN_DMA_LEN;
        _uAuRecBuffOriginAddr = _uAuRecBuffPHYAddr = (UINT32)malloc(_uAuRecBuffSize+32);
        _uAuRecBuffPHYAddr = shift_pointer(_uAuRecBuffOriginAddr,32);
#ifndef ECOS
        if (sysGetCacheState())
        {
            _uAuRecBuffUncachedAddr = _uAuRecBuffPHYAddr | 0x80000000;
        }
        else
            _uAuRecBuffUncachedAddr = _uAuRecBuffPHYAddr;
#else
		_uAuRecBuffUncachedAddr = _uAuRecBuffPHYAddr | 0x80000000;
#endif
        if (_uAuRecBuffPHYAddr!=NULL)
            bretval = TRUE;
        else
            bretval = FALSE;
    }

    return bretval;
}

static VOID audioFreeDMA(WAPI_INOUT apidir)
{
  
    if (apidir==WAPI_OUT)
    {
        if (_uAuPlayBuffOriginAddr!=NULL)
            free((VOID*)_uAuPlayBuffOriginAddr);
    }
    else
    {

        if (_uAuRecBuffOriginAddr!=NULL)
            free((VOID*)_uAuRecBuffOriginAddr);
    }
}


/**************************************************************************/
/*                                                                        */
/* FUNCTION                                                               */
/*      WAV_Open                                                          */
/*                                                                        */
/* DESCRIPTION                                                            */
/*      Open the Audio [In/Out] device                                    */
/*                                                                        */
/* INPUTS                                                                 */
/*      apidir   Data Stream direction device (WAPI_IN, WAPI_OUT)         */
/*                  	                                                  */
/*		pFormat	 Format data structure                                    */
/*               sChannels  -- number of channels (i.e. mono, stereo...)  */
/*               nSamplesPerSec -- sample rate                            */  
/*               pDMABufPTR -- for H/W DMA , it must be 32 bytes alignment*/   
/*               nDMALen -- for H/W DMA , it must be 32 bytes alignment   */
/*               *fnWAVOutCallBack -- for filling the DMA buffer          */                                  
/*               *nWAVInCallBack -- for getting the DMA buffer            */                                 
/*                                                                        */
/*                                                                        */
/* OUTPUTS                                                                */
/*      None                                                              */
/*                                                                        */
/* RETURN                                                                 */
/*      0           Success                                               */
/*		Otherwise	error												  */
/*                                                                        */
/**************************************************************************/
INT  WAV_Open(WAPI_INOUT apidir ,PWAVEFORMATEX pFormat, AU_DEV_E eDev)
{
    INT nRetVal=0;
    if (g_fInUse[apidir]) 
    {
        _debug_msg("device is already in use!!\r\n");
        nRetVal =  ERR_DEV_ACTIVE;
        goto error;
    }
    
    //_uAuPlayBuffAddr = (UINT32)pFormat->pDMABufPTR;
    //_uAuPlayBuffSize = pFormat->nDMALen;
    
    if (audioAllocDMA(apidir)==FALSE)
    {
        _error_msg("DMA alloc failed!!\n");
        nRetVal = ERR_AU_DMA_ALLOC;
        goto error;
    } 
    
    outpw(REG_CLKEN,inpw(REG_CLKEN) | 0x2);//audio clock enable
    if (apidir < NUM_API_DIRS) g_fInUse[apidir] = TRUE;
#ifdef HAVE_AC97
    if (eDev == AU_DEV_AC97)
       	nRetVal = Initialize_AC97(apidir ,pFormat);
#endif

#ifdef HAVE_I2S
    if (eDev == AU_DEV_I2S)
        nRetVal = Initialize_I2S(apidir ,pFormat);
#endif

    if (nRetVal<0)
        goto error;
    return nRetVal;
    
error:

    //g_fInUse[apidir] = FALSE;
    //audioFreeDMA(apidir);
    //outpw(REG_CLKEN,inpw(REG_CLKEN) &~ 0x2);//audio clock disable
    return nRetVal;
    
}

INT WAV_Close(WAPI_INOUT apidir,AU_DEV_E eDev)
{
    INT nRetVal;
    if (!g_fInUse[apidir]) 
	{
        _debug_msg("device is not in use!!\r\n");
        return ERR_DEV_NOT_ACTIVE;
    }

    audioFreeDMA(apidir);
    
    nRetVal = WAV_IOControl(apidir,WPDM_STOP,0,0,eDev);
    outpw(REG_CLKEN,inpw(REG_CLKEN) &~ 0x2);//audio clock disable
    g_fInUse[apidir] = FALSE;    
    return nRetVal;
    
}
INT WAV_Write(UINT32 dwData,
               VOID* pBuf,
               UINT32 Len)
{
    return 0;
}
INT WAV_Read(UINT32 dwData,
               VOID* pBuf,
               UINT32 Len)
{
    return 0;
}
/*************************************************************************/
/*                                                                       */
/* FUNCTION                                                              */
/*      WAV_IOControl                                                    */
/*                                                                       */
/* DESCRIPTION                                                           */
/*      NUC900 Audio I/O control functions                              */
/*                                                                       */
/* INPUTS                                                                */
/*      apidir    Data Stream direction device (WAPI_IN, WAPI_OUT)       */
/*                  	                                                 */
/*		nCode		                                                     */
/*                  WPDM_OPEN                                            */ 
/*                  WPDM_CLOSE                                           */
/*                  WPDM_GETVOLUME                                       */
/*                  WPDM_SETVOLUME                                       */
/*                  WPDM_START                                           */
/*                  WPDM_STOP                                            */
/*      nParam1,nParam2                                                  */
/*                Used by specific IO control code                       */
/*      eDev        AU_DEV_AC97,  AU_DEV_I2S 2 devices supported        */   
/* OUTPUTS                                                               */
/*      None                                                             */
/*                                                                       */
/* RETURN                                                                */
/*      0           Success                                              */
/*		Otherwise	error												 */
/*                                                                       */
/*************************************************************************/
INT  WAV_IOControl(
    WAPI_INOUT apidir,
    UINT32      nCode,
    UINT32      nParam1,
    UINT32      nParam2,
    AU_DEV_E eDev
    )
{
	if (g_fInUse[apidir]==FALSE) 
	{
        _debug_msg("device is not ready!!\r\n");
        return ERR_DEV_NOT_ACTIVE;
    }
    switch(nCode)
    {
        case WPDM_OPEN:
            return WAV_Open(apidir,(PWAVEFORMATEX)nParam1,eDev);
            break;
        case WPDM_CLOSE:
            return WAV_Close(apidir,eDev);
            break;
        case WPDM_GETVOLUME:
        case WPDM_SETPLAYVOLUME:
        case WPDM_SETRECVOLUME:        
        case WPDM_START:
        case WPDM_STOP:
#ifdef HAVE_AC97
	        if (eDev == AU_DEV_AC97)
		    return WaveProc_AC97(apidir ,nCode,nParam1,nParam2);
#endif

#ifdef HAVE_I2S
	        if (eDev == AU_DEV_I2S)
		    return WaveProc_I2S(apidir ,nCode,nParam1,nParam2);
#endif
            break;
            
        default:
            return ERR_IOCTL_UNSUPPORT_CODE;
    }
    

    return ERR_IOCTL_UNKNOWN;
	
}


VOID  audioDisable()
{
#if 0
	/* Disable audio clock */
	outpw(REG_CLKCON, inpw(REG_CLKCON) & ~0x10000000);

	/* Config GPIO multi-function control */
	outpw(REG_PADC0, inpw(REG_PADC0) & ~0x20000);
#endif	
}



















